/*
 *   The contents of this file are subject to the Mozilla Public License
 *   Version 1.1 (the "License"); you may not use this file except in
 *   compliance with the License. You may obtain a copy of the License at
 *   http://www.mozilla.org/MPL/
 *
 *   Software distributed under the License is distributed on an "AS IS"
 *   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *   License for the specific language governing rights and limitations
 *   under the License.
 *
 *   The Original Code is Matra - the DTD Parser.
 *
 *   The Initial Developer of the Original Code is Conrad S Roche.
 *   Portions created by Conrad S Roche are Copyright (C) Conrad 
 *   S Roche. All Rights Reserved.
 *
 *   Alternatively, the contents of this file may be used under the terms
 *   of the GNU GENERAL PUBLIC LICENSE Version 2 or any later version
 *   (the  "[GPL] License"), in which case the
 *   provisions of GPL License are applicable instead of those
 *   above.  If you wish to allow use of your version of this file only
 *   under the terms of the GPL License and not to allow others to use
 *   your version of this file under the MPL, indicate your decision by
 *   deleting  the provisions above and replace  them with the notice and
 *   other provisions required by the GPL License.  If you do not delete
 *   the provisions above, a recipient may use your version of this file
 *   under either the MPL or the GPL License."
 *
 *   [NOTE: The text of this Exhibit A may differ slightly from the text of
 *   the notices in the Source Code files of the Original Code. You should
 *   use the text of this Exhibit A rather than the text found in the
 *   Original Code Source Code for Your Modifications.]
 *
 * Created: Conrad S Roche <derupe at users.sourceforge.net>,  27-Sep-2003
 */
package com.conradroche.matra.parser;

import com.conradroche.dtd.decl.ElementDecl;
import com.conradroche.dtd.parser.ElementDeclReader;
import com.conradroche.matra.data.DTDData;
import com.conradroche.matra.exception.DTDSyntaxException;

/**
 * Class to read Element declarations.
 *
 * @author Conrad Roche
 */
public class ElementDeclReaderImpl implements ElementDeclReader {

	/**
	 * Token identifying an element type declaration.
	 */
	private static final String ELEMENT_DECL_START = "<!ELEMENT";
	private static final String EMPTY_CONTENT = "EMPTY";
	private static final String ANY_CONTENT   = "ANY";
	
	/**
	 * ElementDeclReaderImpl Constructor.
	 */
	public ElementDeclReaderImpl() {
		super();
	}

	/**
	 * Read the Element declaration from the
	 * data stream.
	 * 
	 * @param data The stream from which to read the
	 * 			element declaration.
	 * 
	 * @return The element declaration.
	 * 
	 * @throws DTDSyntaxException If the Element 
	 * 			declaration contains a syntax
	 * 			error. 
	 * 
	 * @see com.conradroche.dtd.parser.ElementDeclReader#readElementDecl(com.conradroche.matra.data.DTDData)
	 */
	public ElementDecl readElementDecl(DTDData data)
		throws DTDSyntaxException {
		
		if(!isElementDeclStart(data)) {
			return null;
		}
		
		//[45]    elementdecl    ::=    '<!ELEMENT' S Name S contentspec S? '>'
		
		//CR: TODO: Instantiate an Impl of ElementDecl.
		ElementDecl element = null; 
		
		data.getNextToken(); // '<!ELEMENT'
		data.skipWhiteSpace(); // S
		
		String elemName = data.getNextName(); // Name
		element.setElementName(elemName);
		
		data.skipWhiteSpace(); // S
		
		readContentSpec(element, data); // contentspec
		
		data.skipWhiteSpace(); // S?
		
		if(data.lookNextChar() != '>') {
			throw new DTDSyntaxException("Invalid Element block encountered at location " + 
						data.getCurrentLocation() + ". Was expecting '>', got char '" + data.lookNextChar() + "'."); 
		}

		data.getNextChar(); // '>'
		
		return element;
	}
	
	/**
	 * CR: JavaDoc: Add javadoc for ElementDeclReaderImpl::readContentSpec
	 * 
	 * @param element
	 * @param data
	 * 
	 * @throws DTDSyntaxException 
	 */
	private void readContentSpec(ElementDecl element, DTDData data) throws DTDSyntaxException {
		// CR: TODO: Implement ElementDeclReaderImpl::readContentSpec
		//[46]    contentspec    ::=    'EMPTY' | 'ANY' | Mixed | children 
		
		//the mixed and children content models start with a '('
		if(data.lookNextChar() == '(') {
			//CR: TODO: invoke methods to read the content.
		} else {
			//CR: NOTE: I expect more 'empty' content than 'any'; hence checking that first.
			if(data.nextStringEquals(EMPTY_CONTENT)) {
				
				data.skipChars(EMPTY_CONTENT.length());
				//CR: TODO: fill in the content model
				
			} else if(data.nextStringEquals(ANY_CONTENT)) {
				
				data.skipChars(ANY_CONTENT.length());
				//CR: TODO: fill in the content model
				
			} else {
				throw new DTDSyntaxException("Invalid content model encountered in the element declaration for " +
							element.getElementName() + " - '" + data.getNextToken() + "'.");
			}
		}
	}

	/**
	 * Checks if there is an element declaration
	 * at the beginning of the data.
	 * 
	 * @param data The stream from which to read the
	 * 			element declaration.
	 * 
	 * @return <code>true</code> if there is an 
	 * 		element declaration at the start of
	 * 		the data stream; <code>false</code>
	 * 		otherwise.
	 */
	public boolean isElementDeclStart(DTDData data) {
		
		if(data == null || data.endOfData()) {
			return false;
		}
		
		return data.nextTokenEquals(ELEMENT_DECL_START);
	}
}
